﻿using Application.CommonDto;
using Application.UserServices.Dto;
using Entity;
using Entity.BaseEntitys;
using Entity.Permissionss;
using Entity.RolePermissionss;
using Entity.UserPermissionss;
using Entity.UserRoles;
using EntityFrameWorkCore;
using FrameWorkCore;
using Microsoft.AspNetCore.Mvc;
using Repository.Repository;
using ShardingCore.Extensions.ShardingPageExtensions;
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Utility;
using Utility.Helper;

namespace Application.UserServices
{
    public interface IUserService
    {

    }

    public class UserService: ApplicationServiceBase, IUserService
    {
        public IRepository<UserPermissions, Guid, DefaultDbContext> UserPermissionsRep { get; set; }

        public IRepository<RolePermissions, Guid, DefaultDbContext> RolePermissionsRep { get; set; }

        public IRepository<Permissions, Guid, DefaultDbContext> PermissionsRep { get; set; }

        public async ValueTask<UserInfoDto> GetUserInfo()
        {
            var user = await base.GetCurrentUserAsync();
            var roles = await base.GetCurrentUserRolesAsync();
            var outPut = base.MapTo<UserInfoDto>(user);
                outPut.roles = roles.Select(x => x.RoleName).ToList();
            if (user.UserName == "admin" || outPut.roles.Contains("admin"))
                outPut.Permissionss = (await PermissionsRep.GetAllListAsync()).Select(x => x.PermissionsCode).ToList();
            else
            {
                var roleIds = roles.Select(x => x.Id);
                var rolePerIds = (await RolePermissionsRep.GetAllListAsync(x => roleIds.Contains(x.RoleId)))
                    .GroupBy(x => x.PermissionsId).Select(x => x.Key);
                var userPerIds =  (await UserPermissionsRep.GetAllListAsync(x => x.UserId == user.Id)).Select(x => x.PermissionsId);
                var perIds = rolePerIds.Union(userPerIds);
                outPut.Permissionss = (await PermissionsRep.GetAllListAsync(x => perIds.Contains(x.Id))).Select(x => x.PermissionsCode).ToList();
            }
           
            outPut.PermissionsMenus = await PermissionsRep.GetAllListAsync();

            return outPut;
        }


        public async ValueTask TestDataSource()
        {
            var users = new List<User>();
            for (int i = 0; i < 10000; i++)
            {
                users.Add(new User
                {
                    Id = GuidExtension.NewGuid(),
                    UserName = "123456" + i,
                    PassWord = DESEncryptExtension.Encrypt("123456"),
                    EmailAddress = "1486971629@qq.com"
                });
                
            }
            await UserRep.BatchInsertAsync(users);


        }

     
            

        public async ValueTask CreateUser(CreateUserDto dto)
        {
            try
            {

                if ((await UserRep.FirstOrDefaultAsync(x => x.UserName.Trim().ToLower().Equals(dto.UserName.Trim().ToLower()))) is not null)
                    throw new Exception("The user has existed! Add a failure!");

                dto.PassWord = DESEncryptExtension.Encrypt(dto.PassWord);
                var user = await base.UserRep.InsertAsync(base.MapTo<User>(dto));
                if (dto.PermissionsIds.Any())
                    await UserPermissionsRep.BatchInsertAsync(
                        dto.PermissionsIds.Select(x => 
                         new UserPermissions 
                         { 
                             Id = GuidExtension.NewGuid(),
                             UserId = user.Id, 
                             PermissionsId = x 
                         }).ToList());

                if (dto.RoleIds.Any())
                    await UserRoleRep.BatchInsertAsync(
                        dto.RoleIds.Select(x =>
                         new UserRole
                         {
                             Id = GuidExtension.NewGuid(),
                             UserId = user.Id,
                             RoleId = x
                         }).ToList());
            }
            catch (Exception e)
            {

                throw new UserFriendlyException(e.Message);
            }

            
        }

        public async ValueTask DeleteUser(Entity<Guid> dto)
        {
            var user = await UserRep.FirstOrDefaultAsync(x => x.Id == dto.Id && x.SplitArea == dto.SplitArea );
            if (user is null) throw new UserFriendlyException("The user does not exist, delete failed!");
            if (user.UserName.ToLower() == "admin") throw new UserFriendlyException("The current for the system administrator account, delete failed!");

            await base.UserRep.DeleteAsync(user);
            await base.UserRoleRep.BatchDeleteAsync(x => x.UserId == dto.Id);
            await UserPermissionsRep.BatchDeleteAsync(x => x.UserId == dto.Id);
        }

        public async ValueTask UpdateUser(UpdateUserDto dto)
        {
 
            var user = await UserRep.FirstOrDefaultAsync(x => x.Id == dto.Id && x.SplitArea == dto.SplitArea);
            base.Map<User>(dto, user);            
            await UserRep.UpdateAsync(user);

            await UserPermissionsRep.BatchDeleteAsync(x => x.UserId == user.Id);
            await UserRoleRep.BatchDeleteAsync(x => x.UserId == user.Id);
            await UnitOfWorkManager.SaveActiveUnitOfWorkAsync();

            if (dto.PermissionsIds.Any())
                await UserPermissionsRep.BatchInsertAsync(
                    dto.PermissionsIds.Select(x => 
                     new UserPermissions 
                     {
                         Id = GuidExtension.NewGuid(),
                         UserId = user.Id, 
                         PermissionsId = x 
                     }).ToList());

            if (dto.RoleIds.Any())
                await UserRoleRep.BatchInsertAsync(
                    dto.RoleIds.Select(x =>
                     new UserRole
                     {
                         Id = GuidExtension.NewGuid(),
                         UserId = user.Id,
                         RoleId = x
                     }).ToList());
        }


        public async ValueTask<ShardingPagedResult<UserByPageOutDto>> GetUserByPage(UserByPageQueryDto dto)
        {

             var all = UserRep.GetAll()
             .QaWhereIf(!dto.UserName.IsNullOrEmpty(), x => x.UserName.Contains(dto.UserName))
             .QaWhereIf(!dto.EmailAddress.IsNullOrEmpty(), x => x.EmailAddress.Contains(dto.EmailAddress))
             .QaWhereIf(!dto.Phone.IsNullOrEmpty(), x => x.Phone.Contains(dto.Phone));

             var res = from item in all
                       select new UserByPageOutDto
                       {
                           Id = item.Id,
                           UserName = item.UserName,
                           EmailAddress = item.EmailAddress,
                           Phone = item.Phone,
                           CreateTime = item.CreateTime,
                           TimeStamp = item.TimeStamp,
                           SplitArea = item.SplitArea                          
                       };

            var outPut =  await res.QaPagedResult(dto);
            var userIds = outPut.Data.Select(x => x.Id);
            var userRoles = await UserRoleRep.GetAllListAsync(x => userIds.Contains(x.UserId));
            var roleIds = userRoles.Select(x => x.RoleId);
            var roles = await RoleRep.GetAllListAsync(x => roleIds.Contains(x.Id));
            //好像不支持这样写x => userRoles.Select(c => c.RoleId).Contains(x.Id)
            outPut.Data.ForEach(x => 
            { 
                x.RoleIds = userRoles.Where(c => c.UserId == x.Id).Select(d => d.RoleId).ToArray();
                x.RoleNames = string.Join(';', roles.Where(c => x.RoleIds.Contains(c.Id)).Select(d => d.RoleName));
            });

            return outPut;

        }

        public async ValueTask<List<Guid>> GetUserPermissionsIds(EntityDto<Guid> dto)
           => (await UserPermissionsRep.GetAllListAsync(x => x.UserId == dto.Id)).Select(x => x.PermissionsId).ToList();

        public async ValueTask<List<Guid>> GetUserRoleIds(EntityDto<Guid> dto)
           => (await UserRoleRep.GetAllListAsync(x => x.UserId == dto.Id)).Select(x => x.RoleId).ToList();

    }
}
